package cn.bone.sentinel.web;

import cn.bone.sentinel.annotation.Api;
import cn.bone.sentinel.bean.CommonResult;
import cn.bone.sentinel.bean.Person;
import cn.bone.sentinel.conf.SpringFactory;
import cn.bone.sentinel.util.ParamUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.esotericsoftware.reflectasm.MethodAccess;
import jodd.bean.BeanUtil;
import jodd.util.StringUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/demo")
public class DemoController {


    @RequestMapping("/test01")
    @SentinelResource(value="bone-test01",blockHandler = "test01Handler", fallback = "test01FallBack")
    public String test01(Long s){
        System.out.println("test01");
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "test01";
    }

    public String test01Handler(Long s, BlockException ex){
        System.out.println("test01 Handler");
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }

    public String test01FallBack(Long s){
        System.out.println("test01 fallback");
        return "test01 fallback";
    }

    @RequestMapping("/test02")
    @SentinelResource(value="bone-test02",blockHandler = "test02Handler", fallback = "test02FallBack")
    public String test02(){
        System.out.println("test02");
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "test02";
    }

    public String test02Handler(BlockException ex){
        System.out.println("test02 Handler");
        ex.printStackTrace();
        return "Oops, error occurred at test02";
    }

    public String test02FallBack(){
        System.out.println("test02 fallback");
        return "test02 fallback";
    }



    @PostConstruct
    public void init(){
        flowRule();
        degradeRule();
    }

    public void flowRule(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource("bone-test01");
        rule1.setCount(1);
        rules.add(rule1);

        FlowRule rule2 = new FlowRule();
        rule2.setResource("bone-test02");
        rule2.setCount(1);
        rules.add(rule2);

        FlowRuleManager.loadRules(rules);
    }

    public void degradeRule(){
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule1 = new DegradeRule();
        rule1.setResource("bone-test01");
        rule1.setCount(1000);
        rule1.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule1.setTimeWindow(2);
        rules.add(rule1);

        DegradeRule rule2 = new DegradeRule();
        rule2.setResource("bone-test02");
        rule2.setCount(2000);
        rule2.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule2.setTimeWindow(1);
        rules.add(rule2);
        DegradeRuleManager.loadRules(rules);
    }

    @RequestMapping("test03")
    public String test03(HttpServletRequest request){

        Object o = null;
        Class<Person> personClass = Person.class;
        try {
            o = personClass.getConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        Field[] fields = personClass.getDeclaredFields();
        try{
            for(Field field : fields){
                String parameter = request.getParameter(field.getName());
                if(StringUtil.isEmpty(parameter)){
                    continue;
                }
                BeanUtil.pojo.setProperty(o,field.getName(),parameter);
            }
        }catch (Exception e){
            throw new RuntimeException("参数类型错误");
        }

        System.out.println(o);
        return o.toString();
    }


    @RequestMapping("/person")
    public CommonResult<?> person(HttpServletRequest request){

        String className = "cn.bone.sentinel.service.IDemoService";
        String methodName = "hello";
        CommonResult<?> result = reflectasmInvoke(className,methodName,request);
        return result;
    }

    /**使用spring的 ReflectionUtils 来完成反射调用*/
    private CommonResult<?> springInvoke(String className,String methodName,HttpServletRequest request){
        Class<?> serviceClass = null;
        try {
            serviceClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object bean = SpringFactory.getBean(serviceClass);
        Method[] methods = serviceClass.getMethods();
        Method targetMethod = null;
        for(Method method : methods){
            if(method.getName().equals(methodName)){
                targetMethod = method;
                break;
            }
        }
        Annotation[] annotations = targetMethod.getAnnotations();
        System.out.println("annotations");
        for (Annotation annotation : annotations){
            String annotationName = annotation.getClass().getName();
            System.out.println(annotationName);
            String name2 = annotation.annotationType().getName();
            System.out.println(name2);
        }

        //取接口方法的注解
        Api api = AnnotationUtils.findAnnotation(targetMethod, Api.class);
        String apiValue = api.value();
        System.out.println(apiValue);

        Annotation[] annotations1 = AnnotationUtils.getAnnotations(targetMethod);
        for(Annotation annotation : annotations1){
            System.out.println(annotation.annotationType().getName());
        }

        Class<?>[] parameterTypes = targetMethod.getParameterTypes();

        Object result = null;
        //有参数
        if(parameterTypes.length>0){
            Object param = ParamUtil.getParam(parameterTypes[0], request);
            result =  ReflectionUtils.invokeMethod(targetMethod,bean,param);
        }
        //无参数
        else{
            result = ReflectionUtils.invokeMethod(targetMethod,bean);
        }
        return (CommonResult<?>) result;
    }

    /**使用 reflectasm 库来完成反射调用*/
    private CommonResult<?> reflectasmInvoke(String className,String methodName,HttpServletRequest request){

        MethodAccess methodAccess;
        Class<?> targetClass = null;
        CommonResult<?> result;
        try {
            targetClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object bean = SpringFactory.getBean(targetClass);
        methodAccess = MethodAccess.get(targetClass);
        int methodIndex = methodAccess.getIndex(methodName);
        Class[] paramTypes = methodAccess.getParameterTypes()[methodIndex];
        Object[] args = null;
        if(paramTypes.length==1){
            Object param = ParamUtil.getParam(paramTypes[0], request);
            args = new Object[]{param};
        }
        result = (CommonResult<?>) methodAccess.invoke(bean,methodName,args);
        return result;
    }
}
